#pragma once


#include <list>
#include <string>

#include "Threads.h"
#include "TinyMsg.h"

enum EMSGTYPE{
	MSGTYPE_UNKOWN=0,
	MSGTYPE_HB=1
};

#define HBMSGVERSION 1000
#define UNKOWNMSGVERSION 0

enum TCPMSGTYPE{
			TCPMSGTYPE_MSG =0,
			TCPMSGTYPE_DATA =1
		};

namespace TinyCC
{
	namespace msg
	{

		static char g_MagicKey[]	 = "^CA@";
		static char g_MagicEnd[]	 = "@AC^";
		class CTinyTCPMsgHead
		{
		public:
			CTinyTCPMsgHead()
				:m_dwMsgLength( 0 )
				,m_dataType(0)
			{
				memcpy( MagicKey, g_MagicKey, 4 );
				memset( m_DestINS, 0, 8 );
				memset( m_SourceINS, 0, 8 );
				memset( reserved, 0, 32 );
			}
		public:
			unsigned char MagicKey[4];
			unsigned char m_DestINS[ 8 ];
			unsigned char m_SourceINS[ 8 ];
			DWORD m_dataType;
			DWORD m_dwMsgLength;
			unsigned char reserved[32];

		};

		class CTinyTCPRawMsg
		{
		public:
			char* m_pData;
			long m_lDataLength;
			SOCKET m_socketID;	

			CTinyTCPRawMsg()
				:m_pData( NULL )
				,m_lDataLength( 0 )
			{

			}

			void setDataLength( long len )
			{
				m_lDataLength = len;
				if( m_lDataLength > 0 )
					m_pData = new char[m_lDataLength];
			}

			~CTinyTCPRawMsg()
			{
				if( m_pData != NULL )
				{
					delete[] m_pData;
					m_pData = NULL;
				}
			}
		};

		class CTinyTCPMsg
		{
		private :
			//char m_Buf[4096];
		public:
			void* m_pData;
			long m_lDataLength;
			SOCKET m_socketID;	
			CTinyTCPMsgHead m_msgHead;
			char* m_pSerializedData;
			//CTinyMsg m_innermsg;
		public:

			CTinyTCPMsg()
				:m_pData( NULL )
				,m_lDataLength( 0 )
				,m_socketID( 0 )
				,m_pSerializedData( NULL )
			{
				//m_pData = m_Buf;
			}

			~CTinyTCPMsg()
			{
				if( m_pSerializedData != NULL )
				{
					delete[] m_pSerializedData;
					m_pSerializedData = NULL;
				}
				if( m_pData != NULL )
				{
					delete[] m_pData;
					m_pData = NULL;
				}
			}


			DWORD getDataType(){return m_msgHead.m_dataType;}
			void setDataType(DWORD type){m_msgHead.m_dataType = type; }
			inline const char* getDestIns(){	return (const char*)m_msgHead.m_DestINS;	}
			inline void setDestIns( const char* value )
			{
				memset( m_msgHead.m_DestINS, 0, 8 );
				if( value == NULL )
					return;
				int strLength = (int)strlen( value );
				if( strLength>= 8 )
					strLength = 7;
				memcpy( m_msgHead.m_DestINS, value, strLength );
			}

			inline const char* getSourceIns(){	return (const char*)m_msgHead.m_SourceINS;	}
			inline void setSourceIns( const char* value )
			{
				memset( m_msgHead.m_SourceINS, 0, 8 );
				if( value == NULL )
					return;
				int strLength = (int)strlen( value );
				if( strLength>= 8 )
					strLength = 7;
				memcpy( m_msgHead.m_SourceINS, value, strLength );
			}

			const char* package(long &length,CTinyMsg* msg);

			bool unPackage( CTinyTCPRawMsg& rawMsg,CTinyMsg* msg );
			//add this to parse the msghead
			static bool ParseMsgHead(CTinyTCPMsgHead& head, const char* pMsg, long len);
		};

		// add TCPAtom and support TCPHB mechanism

		//AtomTCP Msg key
		static char g_AtomMagicKey[]  = "^CZ@";
		static char g_AtomMagicEnd[]  = "@ZC^";


		//TCP Head class act as Father of Atom Msg Head classes
		class CTCPHead
		{
		public:
			WORD m_wMsgVersion;
			WORD m_wMsgFunc;
			WORD m_wMsgType;
			WORD m_wMsgName;				
			unsigned char MagicKey[4];
			unsigned char m_DestINS[ 8 ];
			unsigned char m_SourceINS[ 8 ];
			DWORD m_dwMsgLength;
			unsigned char reserved[28];
		public:
			CTCPHead()
				:m_wMsgVersion(UNKOWNMSGVERSION)
				,m_wMsgFunc(MSGTYPE_UNKOWN)
				,m_wMsgType(0)
				,m_wMsgName(0)
				,m_dwMsgLength(0)
			{
				memset( m_DestINS, 0, 8 );
				memset( reserved, 0, 28 );
				memcpy( MagicKey, g_AtomMagicKey, 4 );
			}

		};

		//TCPHB Head derived by TCPHead as a kind of Atom Msg Head
		//TCPHB Head Msg
		class CTCPHBHead: public CTCPHead
		{
		public:
			CTCPHBHead()
			{
				m_wMsgVersion=HBMSGVERSION;
				m_wMsgFunc=MSGTYPE_HB;
			}
		};

		//TCPAtomMsg
		template< typename T=CTCPHead>
		class CTCPAtomMsg
		{
		private :
			//char m_Buf[4096];
		public:
			void* m_pData;
			long m_lDataLength;
			SOCKET m_socketID;	
			T m_msgHead;
			char* m_pSerializedData;
			CTinyMsg m_uvmsg;
		public:

			CTCPAtomMsg():
			  m_pData( NULL )
				  ,m_lDataLength( 0 )
				  ,m_socketID( 0 )
				  ,m_pSerializedData( NULL )
			  {
				  //m_pData = m_Buf;

			  }

			  ~CTCPAtomMsg()
			  {
				  if( m_pSerializedData != NULL )
				  {
					  delete[] m_pSerializedData;
					  m_pSerializedData = NULL;
				  }
				  if( m_pData != NULL )
				  {
					  delete[] m_pData;
					  m_pData = NULL;
				  }
			  }

			  inline WORD getMsgVersion() const{	return m_msgHead.m_wMsgVersion;	}
			  inline WORD getMsgFunc() const{	return m_msgHead.m_wMsgFunc;	}
			  inline WORD getMsgType() const{	return m_msgHead.m_wMsgType;	}
			  inline void setMsgType( WORD value ){	m_msgHead.m_wMsgType = value;	}

			  inline WORD getMsgName() const{	return m_msgHead.m_wMsgName;	}
			  inline void setMsgName( WORD value ) {	m_msgHead.m_wMsgName = value;	}

			  inline const char* getDestIns(){	return (const char*)m_msgHead.m_DestINS;	}
			  inline void setDestIns( const char* value )
			  {
				  memset( m_msgHead.m_DestINS, 0, 8 );
				  if( value == NULL )
					  return;
				  int strLength = strlen( value );
				  if( strLength>= 8 )
					  strLength = 7;
				  memcpy( m_msgHead.m_DestINS, value, strLength );
			  }

			  inline const char* getSourceIns(){	return (const char*)m_msgHead.m_SourceINS;	}
			  inline void setSourceIns( const char* value )
			  {
				  memset( m_msgHead.m_SourceINS, 0, 8 );
				  if( value == NULL )
					  return;
				  int strLength = strlen( value );
				  if( strLength>= 8 )
					  strLength = 7;
				  memcpy( m_msgHead.m_SourceINS, value, strLength );
			  }

			  const char* serialize(long &length,CTinyMsg* msg=NULL );

			  int deSerialize( CTinyTCPRawMsg& rawMsg );
			  //add this to parse the msghead
			  static bool ParseMsgHead(CTCPHead& head, const char* pMsg, long len);
		};
		//end TCPHBHead

		//implemention Atom MSG Function
		template <typename T>
		int CTCPAtomMsg<T>::deSerialize( CTinyTCPRawMsg& rawMsg )
		{
			if( rawMsg.m_lDataLength >= sizeof( m_msgHead ) )
			{
				char* pDataCursor = (char*)rawMsg.m_pData;
				char* pDataBody = (char*)rawMsg.m_pData + sizeof( m_msgHead );
				memcpy( (void*)(m_msgHead.MagicKey), rawMsg.m_pData, 4 );
				pDataCursor += 4;
				WORD* pWord = ( WORD* )pDataCursor;
				m_msgHead.m_wMsgType = ntohs( *pWord );
				pDataCursor += 2;

				pWord = ( WORD* )pDataCursor;
				m_msgHead.m_wMsgName = ntohs( *pWord );
				pDataCursor += 2;

				memcpy( m_msgHead.m_DestINS, pDataCursor, 8 );
				pDataCursor += 8;

				memcpy( m_msgHead.m_SourceINS, pDataCursor, 8 );
				pDataCursor += 8;

				DWORD* pDWord = ( DWORD* )pDataCursor;
				m_msgHead.m_dwMsgLength = ntohl( *pDWord );
				pDataCursor += 4;

				//m_wMsgFunc and m_wMsgVersion Data
				pWord = ( WORD* )pDataCursor;
				m_msgHead.m_wMsgFunc = ntohs( *pWord );
				pDataCursor += 2;
				pWord = ( WORD* )pDataCursor;
				m_msgHead.m_wMsgVersion = ntohs( *pWord );
				pDataCursor += 2;

				if( m_msgHead.m_dwMsgLength > (rawMsg.m_lDataLength - sizeof( m_msgHead ) - 4) )
				{
					//g_Debug.debugPrint( "Msg length incorrect!" );
					return 1;
				}
				else
				{

					if( memcmp( m_msgHead.MagicKey, g_AtomMagicKey, 4 ) == 0 )
					{
						if( m_msgHead.m_dwMsgLength <= 40960 )
						{
							if( m_pData != NULL )
							{
								delete[] m_pData;
								m_pData = NULL;
							}
							m_pData = new char[ m_msgHead.m_dwMsgLength ];
							memcpy( m_pData, pDataBody, m_msgHead.m_dwMsgLength );
							m_lDataLength = m_msgHead.m_dwMsgLength;
							this->m_uvmsg.deSerialize((char *)m_pData, m_msgHead.m_dwMsgLength);
							char* msgEnd = pDataBody + m_msgHead.m_dwMsgLength;
							if( memcmp( msgEnd, g_AtomMagicEnd, 4 ) == 0 )
							{
								//g_AgentProxy. processTCPMsg( rawMsg.m_socketID, msg );
								//delete &rawMsg;
								return 0;
							}
							else
							{
								//g_Debug.debugPrint( "Msg end string invalid." );
								//delete &rawMsg;
								return 2;
							}
						}
						else
						{
							//g_Debug.debugPrint( "Msg length is large than 40960." );
							//delete &rawMsg;
							return 3;
						}
					}
					else
					{
						//g_Debug.debugPrint( "Receive invalid tcp " );
						//delete &rawMsg;
						return 4;
					}
				}
			}
			return 4;
		}
		template <typename T>
		const char* CTCPAtomMsg<T>::serialize(long &length,CTinyMsg* msg)
		{
			const char * p_serial=NULL;
			if(msg==NULL)
			{
				p_serial=m_uvmsg.serialize(m_lDataLength);
			}
			else
			{
				p_serial=msg->serialize(m_lDataLength);
			}


			//p_serial = (p_serial+4);
			//m_lDataLength-=4;
			m_msgHead.m_dwMsgLength=m_lDataLength;

			if(m_pData !=NULL)
			{
				delete[] m_pData;
				m_pData =NULL;
			}
			m_pData =new char[m_lDataLength];
			memcpy( m_pData, p_serial, m_lDataLength );

			if( m_pData == NULL  )
				return NULL;

			//Calculate the size of memory

			long serializeSize =  sizeof(CTCPHead) + m_msgHead.m_dwMsgLength + 4;
			length = serializeSize;

			if(  m_pSerializedData != NULL)
			{
				delete[] m_pSerializedData;
				m_pSerializedData = NULL;
			}
			m_pSerializedData = new char[ serializeSize ];

			char* pCursor = m_pSerializedData;
			/*memcpy( m_pSerializedData, &m_msgHead, sizeof(CTinyTCPMsgHead) );
			pCursor += sizeof( CTinyTCPMsgHead );*/

			memcpy( m_pSerializedData, m_msgHead.MagicKey, 4 );
			pCursor += 4;

			WORD* pWord = (WORD*)pCursor;
			*pWord = htons( m_msgHead.m_wMsgType );
			pCursor += 2;

			pWord = (WORD*)pCursor;
			*pWord = htons( m_msgHead.m_wMsgName );
			pCursor += 2;

			memcpy( pCursor, m_msgHead.m_DestINS, 8 );
			pCursor += 8;

			memcpy( pCursor, m_msgHead.m_SourceINS, 8 );
			pCursor += 8;

			DWORD* pDWord = (DWORD*)pCursor;
			*pDWord = htonl( m_msgHead.m_dwMsgLength );
			pCursor += 4;

			//add m_wMsgFunc and m_wMsgVersion Data
			//WORD m_wMsgFunc;
			pWord = (WORD*)pCursor;
			*pWord = htons( m_msgHead.m_wMsgFunc );
			pCursor += 2;
			//WORD m_wMsgVersion;
			pWord = (WORD*)pCursor;
			*pWord = htons( m_msgHead.m_wMsgVersion );
			pCursor += 2;

			pCursor += 28;

			memcpy( pCursor, m_pData, m_lDataLength );
			pCursor += m_lDataLength;
			memcpy( pCursor, g_AtomMagicEnd, 4 );


			return m_pSerializedData;
		}
		template <typename T>
		bool CTCPAtomMsg<T>::ParseMsgHead(CTCPHead& head, const char* pMsg, long len)
		{
			if( len >= sizeof( head ) )
			{
				char* pDataCursor = (char*)pMsg;
				memcpy( (void*)(head.MagicKey), pMsg, 4 );
				//judge head?
				pDataCursor += 4;
				WORD* pWord = ( WORD* )pDataCursor;
				head.m_wMsgType = ntohs( *pWord );
				pDataCursor += 2;

				pWord = ( WORD* )pDataCursor;
				head.m_wMsgName = ntohs( *pWord );
				pDataCursor += 2;

				memcpy( head.m_DestINS, pDataCursor, 8 );
				pDataCursor += 8;

				memcpy( head.m_SourceINS, pDataCursor, 8 );
				pDataCursor += 8;

				DWORD* pDWord = ( DWORD* )pDataCursor;
				head.m_dwMsgLength = ntohl( *pDWord );
				pDataCursor += 4;

				//m_wMsgFunc and m_wMsgVersion Data
				pWord = ( WORD* )pDataCursor;
				head.m_wMsgFunc = ntohs( *pWord );
				pDataCursor += 2;
				pWord = ( WORD* )pDataCursor;
				head.m_wMsgVersion = ntohs( *pWord );
				pDataCursor += 2;

				return true;
			}
			else
			{
				return false;
			}
		}
	}
}